/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.contexts;

import cn.easyplatform.FieldNotFoundException;
import cn.easyplatform.dao.BizDao;
import cn.easyplatform.dos.FieldDo;
import cn.easyplatform.dos.Record;
import cn.easyplatform.entities.beans.table.TableBean;
import cn.easyplatform.entities.beans.task.Variable;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.CaseInsensitiveHashMap;
import cn.easyplatform.lang.Nums;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.support.word.ReserveWordFactory;
import cn.easyplatform.type.EntityType;
import cn.easyplatform.type.FieldType;
import cn.easyplatform.type.ScopeType;
import cn.easyplatform.util.RuntimeUtils;

import java.io.Serializable;
import java.util.*;

/**
 * 三级或四级级记录信息
 *
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class RecordContext implements Serializable, Cloneable {

    /**
     *
     */
    private static final long serialVersionUID = -5639435563689800L;

    // 系统变量、自定义变量
    protected Map<String, Object> systemVariables;

    // 自定义变量
    protected Map<String, FieldDo> userVariables;

    // 数据记录
    protected Record record;

    // 记录对应的主KEY值
    protected Object[] keyValues;

    /**
     * @param record
     * @param systemVariables
     * @param userVariables
     */
    public RecordContext(Record record, Map<String, Object> systemVariables,
                         Map<String, FieldDo> userVariables) {
        this(null, record, systemVariables, userVariables);
    }

    /**
     * 列表
     *
     * @param keyValues
     * @param record
     * @param systemVariables
     * @param userVariables
     */
    RecordContext(Object[] keyValues, Record record,
                  Map<String, Object> systemVariables,
                  Map<String, FieldDo> userVariables) {
        this.keyValues = keyValues;
        this.record = record;
        this.systemVariables = new HashMap<String, Object>();
        //this.userVariables = new HashMap<String, FieldDo>();
        this.systemVariables.putAll(systemVariables);
        this.userVariables = userVariables;
/*        if (userVariables != null) {
            for (Map.Entry<String, FieldDo> entry : userVariables.entrySet()) {
                FieldDo fd = entry.getValue();
                if (fd.getScope() == ScopeType.PROTECTED
                        || fd.getScope() == ScopeType.PUBLIC) {
                    if (fd.getShareModel() == Variable.CLONE_VALUE)
                        this.userVariables.put(entry.getKey(), fd.clone());
                    else
                        this.userVariables.put(entry.getKey(), fd);
                }
            }
        }*/
    }

    /**
     * 页面
     *
     * @param systemVariables
     * @param userVariables
     */
    RecordContext(Map<String, Object> systemVariables,
                  Map<String, FieldDo> userVariables) {
        this.systemVariables = systemVariables;
        this.userVariables = userVariables;
    }

    public Record getData() {
        return record;
    }

    void save(CommandContext cc) {
        boolean persist = systemVariables.get("830").equals(
                EntityType.PAGE.getName())
                && !Strings.isBlank(getParameterAsString("833"))
                && getParameterAsBoolean("815")
                && getParameterAsChar("814") != 'R';
        if (persist) {
            TableBean tb = cc.getEntity(getParameterAsString("833"));
            BizDao dao = cc.getBizDao(tb.getSubType());
            char code = getParameterAsChar("814");
            record.setKey(tb.getKey());
            if (code == 'C') {
                dao.insert(cc.getUser(), tb, record, false);
            } else if (code == 'U')
                dao.update(cc.getUser(), tb.getId(), record, false);
            else if (code == 'D') {
                List<FieldDo> key = new ArrayList<FieldDo>(tb.getKey().size());
                for (String name : tb.getKey()) {
                    FieldDo fd = record.get(name);
                    if (fd == null)
                        throw new FieldNotFoundException(
                                "entity.table.field.not.found", tb.getId(),
                                name);
                    key.add(fd);
                }
                dao.delete(cc.getUser(), tb.getId(), key, false);
            }
        }
    }

    public Set<Map.Entry<String, Object>> getSystemVars() {
        return systemVariables.entrySet();
    }

    public Set<String> getSystemNameSet() {
        return systemVariables.keySet();
    }

    public Collection<FieldDo> getUserVars() {
        return userVariables.values();
    }

    public Set<String> getUserVarNameSet() {
        return userVariables.keySet();
    }

    public Collection<FieldDo> getRecordFieldValues() {
        if (record == null)
            return Collections.emptyList();
        return record.getData();
    }

    public Set<String> getRecordNameSet() {
        if (record == null)
            return Collections.emptySet();
        return record.getColumnNames();
    }

    public FieldDo getField(String name) {
        FieldDo field = getFieldQuietly(name);
        if (field == null)
            throw new FieldNotFoundException("entity.table.variable.not.found",
                    name);
        return field;
    }

    public FieldDo getFieldQuietly(String name) {
        if (name.startsWith("&"))
            return ReserveWordFactory.getReserveWordAs(name);
        if (record != null) {
            FieldDo field = record.get(name);
            if (field != null)
                return field;
        }
        FieldDo field = userVariables.get(name.toUpperCase());
        if (field != null)
            return field;
        if (name.length() == 3 && name.compareTo("700") >= 0
                && name.compareTo("899") < 0) {
            Object v = systemVariables.get(name);
            FieldType type = FieldType.cast(v);
            FieldDo fd = new FieldDo(type);
            fd.setName(name);
            fd.setValue(v);
            return fd;
        } else {
            if (Strings.isChineseCharacter(name.charAt(0))) {
                if (record != null) {
                    for (FieldDo f : record.getData()) {
                        if (name.equals(f.getDescription()))
                            return f;
                    }
                }
                for (FieldDo f : userVariables.values()) {
                    if (name.equals(f.getDescription()))
                        return f;
                }
            }
            return null;
        }
    }

    public boolean exists(String name) {
        if (record != null) {
            FieldDo field = record.get(name);
            if (field != null)
                return true;
        }
        if (userVariables.containsKey(name.toUpperCase()))
            return true;
        if (name.length() == 3 && name.compareTo("700") >= 0
                && name.compareTo("899") < 0) {
            if (systemVariables.containsKey(name))
                return true;
        }
        return false;
    }

    public void setValue(String name, Object value) {
        if (record != null) {
            FieldDo field = record.get(name);
            if (field != null) {
                field.setValue(value);
                return;
            }
        }
        FieldDo field = userVariables.get(name.toUpperCase());
        if (field != null) {
            field.setValue(value);
            return;
        }
        if (name.length() == 3 && name.compareTo("700") >= 0
                && name.compareTo("899") < 0)
            systemVariables.put(name, value);
        else {
            if (Strings.isChineseCharacter(name.charAt(0))) {
                if (record != null) {
                    for (FieldDo f : record.getData()) {
                        if (name.equals(f.getDescription())) {
                            f.setValue(value);
                            return;
                        }
                    }
                }
                for (FieldDo f : userVariables.values()) {
                    if (name.equals(f.getDescription())) {
                        f.setValue(value);
                        return;
                    }
                }
            }
            throw new FieldNotFoundException("entity.table.variable.not.found",
                    name);
        }
    }

    public FieldDo createVariable(String name, String type) {
        return createVariable(name, type, null);
    }

    public FieldDo createVariable(String name, String type, String scope) {
        FieldDo var = new FieldDo(FieldType.valueOf(type.toUpperCase()));
        var.setName(name);
        if (scope != null)
            var.setScope(ScopeType.valueOf(scope.toUpperCase()));
        else
            var.setScope(ScopeType.PRIVATE);
        setVariable(var);
        return var;
    }

    public Object[] getKeyValues() {
        return keyValues;
    }

    public void setData(Record data) {
        this.record = null;
        this.record = data;
    }

    public void setVariable(FieldDo var) {
        userVariables.put(var.getName(), var);
    }

    public Object getParameter(String name) {
        return systemVariables.get(name);
    }

    public Object removeParameter(String name) {
        return systemVariables.remove(name);
    }

    public FieldDo removeVariable(String name) {
        return userVariables.remove(name.toUpperCase());
    }

    public void setParameter(String name, Object value) {
        systemVariables.put(name, value);
    }

    public String getParameterAsString(String name) {
        Object obj = systemVariables.get(name);
        if (obj == null)
            return null;
        return obj.toString();
    }

    public Object getValue(String name) {
        if (name.startsWith("$"))
            return ReserveWordFactory.getReserveWord(name);
        else if (name.compareTo("700") >= 0 && name.compareTo("899") < 0)
            return systemVariables.get(name);
        if (record != null) {
            FieldDo field = record.get(name);
            if (field != null)
                return field.getValue();
        }
        FieldDo field = userVariables.get(name.toUpperCase());
        if (field != null)
            return field.getValue();
        else {
            if (Strings.isChineseCharacter(name.charAt(0))) {
                if (record != null) {
                    for (FieldDo f : record.getData()) {
                        if (name.equals(f.getDescription()))
                            return f.getValue();
                    }
                }
                for (FieldDo f : userVariables.values()) {
                    if (name.equals(f.getDescription()))
                        return f.getValue();
                }
            }
            throw new FieldNotFoundException("entity.table.variable.not.found",
                    name);
        }
    }

    public Object getFieldValue(String name) {
        if (record == null)
            throw new FieldNotFoundException("entity.table.field.not.found",
                    systemVariables.get("833"), name);
        FieldDo fd = record.get(name);
        if (fd == null) {
            if (Strings.isChineseCharacter(name.charAt(0))) {
                for (FieldDo f : record.getData()) {
                    if (name.equals(f.getDescription()))
                        return f.getValue();
                }
            }
            throw new FieldNotFoundException("entity.table.field.not.found",
                    systemVariables.get("833"), name);
        }
        return fd.getValue();
    }

    public boolean getParameterAsBoolean(String name) {
        Object obj = systemVariables.get(name);
        if (obj == null)
            return false;
        if (obj instanceof Boolean)
            return ((Boolean) obj).booleanValue();
        return obj.toString().equalsIgnoreCase("true");
    }

    public char getParameterAsChar(String name) {
        Object obj = systemVariables.get(name);
        if (obj == null)
            return ' ';
        if (obj instanceof Character)
            return ((Character) obj).charValue();
        String s = obj.toString();
        if (!Strings.isBlank(s))
            return s.charAt(0);
        return ' ';
    }

    public int getParameterAsInt(String name) {
        Object obj = systemVariables.get(name);
        if (obj == null)
            return 0;
        if (obj instanceof Number)
            return ((Number) obj).intValue();
        return Nums.toInt(obj, 0);
    }

    @Override
    public RecordContext clone() {
        try {
            RecordContext clone = (RecordContext) super.clone();
            clone.systemVariables = new HashMap<String, Object>(this.systemVariables);
            if (userVariables != null) {
                clone.userVariables = new HashMap<>();
                for (Map.Entry<String, FieldDo> entry : userVariables.entrySet())
                    clone.userVariables.put(entry.getKey(), entry.getValue().clone());
            }
            clone.record = this.record == null ? null : this.record.clone();
            return clone;
        } catch (CloneNotSupportedException ex) {
            return null;
        }
    }

    public RecordContext copy() {
        try {
            RecordContext clone = (RecordContext) super.clone();
            clone.systemVariables = new HashMap<String, Object>(this.systemVariables);
            if (userVariables != null) {
                clone.userVariables = new HashMap<>();
                for (Map.Entry<String, FieldDo> entry : userVariables.entrySet()) {
                    FieldDo fd = entry.getValue();
                    if (fd.getShareModel() == Variable.CLONE_VALUE)
                        clone.userVariables.put(entry.getKey(), fd.clone());
                    else
                        clone.userVariables.put(entry.getKey(), fd);
                }
            }
            clone.record = this.record == null ? null : this.record.clone();
            return clone;
        } catch (CloneNotSupportedException ex) {
            return null;
        }
    }

    public void setKeyValues(Object[] keyValues) {
        this.keyValues = keyValues;
    }

    public RecordContext create() {
        return new RecordContext(new Record(), systemVariables, userVariables);
    }

    /**
     * 返回map格式的记录
     *
     * @return
     */
    public Map<String, Object> toMap() {
        Map<String, Object> data = record == null ? new CaseInsensitiveHashMap() : record.toMap();
        for (FieldDo f : userVariables.values())
            data.put(f.getRawName(), f.getValue());
        for (Map.Entry<String, Object> entry : systemVariables.entrySet())
            data.put(entry.getKey(), entry.getValue());
        return data;
    }
}
